﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Media;
using System.Windows;
using System.Windows.Threading;
using AEM.Common;
using AEM.Data;
using AEM.Utils;
using Actions = AEM.Data.BLL.Actions;
using AEM.Properties;

namespace AEM
{
    public static class PortMonitoring
    {
        /// <summary>
        /// 时钟
        /// </summary>
        static DispatcherTimer timer = new DispatcherTimer();
        /// <summary>
        /// COM List 对应的电梯信息
        /// </summary>
        public static List<int[]> liftnums = new List<int[]>();
        /// <summary>
        /// COM List
        /// </summary>
        public static SerialPortManager[] sports;
        /// <summary>
        /// COM数据发送临时工作站
        /// </summary>
        public static List<CommandData>[] commandData;
        /// <summary>
        /// COM List 对应//电梯数组被查询的索引(liftnums[x][queryIndex])
        /// </summary>
        public static int[] queryIndex;
        /// <summary>
        /// COM  超时发指令的时间轴
        /// </summary>
        public static int[] _portTimer;
        /// <summary>
        /// 电机超时未链接设置未链接状态
        /// int[3]
        /// [0]liftnum
        /// [1]liftgroup
        /// [2]sec. (1-10),10 for runing time out,then reset to 1 for a new loop
        /// </summary>
        public static List<int[]> _runOut;
        public static int timeOutStick;
        /// <summary>
        /// 地震标志
        /// </summary>
        public static int[] earthquake;
        public static bool lastearthquake = false;
        public static bool lastIsEarthquaked = false;
        /// <summary>
        /// 警铃
        /// </summary>
        public static SoundPlayer alarmPlayer;
        /// <summary>
        /// 状态驻停 以便故障触发 并点击后 不需要再次触发同状态
        /// </summary>
        public static List<int[]> statusPark = new List<int[]>();

        /// <summary>
        /// 运行标记
        /// </summary>
        public static bool[] LiftRunSign;
        public static bool[] GroupRunSign;
        public static bool MonitoringSign;
        public static int MonitoringIDSign = -1;
        public static bool[] InLineRunSign;

        public static void Init()
        {
            if (null == Config.LiftInftList) return;
            //发送指令
            Actions.SerialPorts.Regulate = doRegulate;
            Actions.SerialPorts.SMSMonitoring = doSMSMonitoring;
            int llength = Config.LiftInftList.Select(x => x.LiftNum).Max();
            LiftRunSign = new bool[llength];
            GroupRunSign = new bool[llength];
            InLineRunSign = new bool[llength];
            earthquake = new int[llength];
            liftnums = new List<int[]>();
            if (null != sports)
            {
                foreach (SerialPortManager sp in sports)
                    sp.Dispose();
            }
            string COMMsg = "";
            string[] ports = (from Data.Model.LiftInfo row in Config.LiftInftList
                              select row.LiftCOM).Distinct().ToArray<string>();  //SerialPort.GetPortNames();//new string[] { "COM1", "COM2" }; //

            sports = new SerialPortManager[ports.Length];
            commandData = new List<CommandData>[ports.Length];
            queryIndex = new int[ports.Length];
            _portTimer = new int[ports.Length];
            int index = 0;
            foreach (string _portname in ports)
            {
                IEnumerable<Data.Model.LiftInfo> result = from r in Config.LiftInftList
                                                          where r.LiftCOM == _portname
                                                          select r;
                List<CommandData> comm = new List<CommandData>();
                foreach (Data.Model.LiftInfo l in result)
                    comm.Add(new CommandData(_portname, Actions.SerialPorts.Query(l.LiftNum, l.LiftType)));

                liftnums.Add((from Data.Model.LiftInfo r in result
                              select r.LiftNum).ToArray<int>());

                commandData[index] = comm;

                SerialPortManager _spManager = new SerialPortManager(_portname);
                _spManager.NewSerialDataRecieved += new SerialPortDataReceivedEventHandler(_spManager_NewSerialDataRecieved);
                _spManager.SendCommandEvent += new EventHandler(_spManager_SendCommandEvent);
                _spManager.SameDataEvent += new SameDataEventHandler(_spManager_SameDataEvent);

                _spManager.Index = index;
                try
                {
                    foreach (Data.Model.LiftInfo l in result)
                        _spManager.StartListening(l.LiftType);
                }
                catch (Exception ex)
                {
                    COMMsg += _portname + ",";
                    Log.WriteError(ex.Message, ex);
                }
                sports[index] = _spManager;

                _portTimer[index] = 0;// _timer;

                index++;
            }
            if (!string.IsNullOrEmpty(COMMsg))
            {
                Splasher.Splash.Topmost = false;
                MessageBox.Show(COMMsg + "注册失败，或已被其它程序使用！", "AEM系统提示", MessageBoxButton.OK, MessageBoxImage.Warning);
            }
            timer = new DispatcherTimer();
            timer.Tick += new EventHandler(timer_Tick);
        }

        public static void Start()
        {
            timer.Interval = TimeSpan.FromMilliseconds(50);
            timeOutStick = Settings.Default.TimeOut;
            timeOutStick = timeOutStick / 100;

            timer.Start();
        }

        static void _spManager_NewSerialDataRecieved(object sender, SerialDataEventArgs e)
        {
            try
            {
                if (null == e || null == e.Data) { return; }
                if (Application.Current.Dispatcher.CheckAccess())
                {
                    // Using this.Invoke causes deadlock when closing serial port, and BeginInvoke is good practice anyway.
                    Application.Current.Dispatcher.BeginInvoke(new EventHandler<SerialDataEventArgs>(_spManager_NewSerialDataRecieved), new object[] { sender, e });
                    return;
                }

                ResetTimeOutTick(e.LiftNum);

                #region 地震状态确认
                if (e.LiftInfo.LiftType!=Data.Enum.LiftType.扶梯)
                {
                    //地震,一台触发则所有触发,所有取消则取消所有
                    earthquake[e.LiftNum - 1] = e.Earthquake ? 1 : 0;
                    if (e.Earthquake)
                    {
                        Config.lastData.Clear();
                        //Log.WriteInf(ConsoleColor.Blue, e.LiftNum + " Earthquake:" + e.Earthquake.ToString());
                    }

                    e.Earthquake = earthquake.ToList().FindIndex(x => x == 1) != -1 ? true : false;

                    if (e.Earthquake != lastearthquake)
                    {
                        lastearthquake = e.Earthquake;
                        Config.lastData.Clear();
                        foreach (Data.Model.LiftInfo i in Config.LiftInftList)
                        {
                            if (null != Actions.SerialPorts.Regulate)
                                Actions.SerialPorts.Regulate.BeginInvoke(e.Earthquake, i.LiftNum, i.LiftCOM,
                                    byte.Parse("4", NumberStyles.HexNumber), 0, new AsyncCallback(Actions.SerialPorts.RegulateCallback), null);
                        }
                        Config.lastData.Clear();
                    }
                }
                
                //if (!e.Earthquake && e.Earthquake == lastearthquake && lastIsEarthquaked != e.Data.IsEarthquaked && (e.Earthquake != e.Data.IsEarthquaked))
                //{
                //    lastIsEarthquaked = e.Data.IsEarthquaked;
                //    Config.lastData.Clear();
                //    foreach (Data.Model.LiftInfo i in Config.LiftInftList)
                //    {
                //        if (null != Actions.SerialPorts.Regulate)
                //            Actions.SerialPorts.Regulate.BeginInvoke(e.Earthquake, i.LiftNum, i.LiftCOM,
                //                byte.Parse("4", NumberStyles.HexNumber), 0, new AsyncCallback(Actions.SerialPorts.RegulateCallback), null);
                //    }
                //    Config.lastData.Clear();
                //}
                #endregion

                CheckPlayAlarm(e);//是否故障鸣铃
                foreach (PeakItem p in PeakMonitoring.peakItems)
                {
                    if (p.LiftNum == e.LiftNum)
                    {
                        p.IsPeek = e.Data.Peak;
                        p.IsUnpeek = !e.Data.Peak;
                        break;
                    }
                }
                if (!e.IsRepeat)
                    Actions.SerialDataLog.Add.BeginInvoke(e, new AsyncCallback(Actions.SerialDataLog.AddCallback), null);//添加数据记录/故障记录,重复则不加

                if (!e.IsRepeat || !LiftRunSign[e.LiftNum - 1])
                    if (null != Actions.LiftInfo.LiftRun)
                        Actions.LiftInfo.LiftRun.BeginInvoke(e, new AsyncCallback(Actions.LiftInfo.LiftRunCallback), null);
                if (!e.IsRepeat || !GroupRunSign[e.LiftNum - 1])
                    if (null != Actions.LiftInfo.LiftRunInGroup)
                        Actions.LiftInfo.LiftRunInGroup.BeginInvoke(e, new AsyncCallback(Actions.LiftInfo.MonitoringCallback), null);
                if (MonitoringIDSign == e.LiftNum && (!e.IsRepeat || MonitoringSign))
                {
                    switch (e.LiftInfo.LiftType)
                    {
                        case Data.Enum.LiftType.电梯:
                            if (null != Actions.LiftInfo.LiftMonitoring)
                                Actions.LiftInfo.LiftMonitoring.BeginInvoke(e, new AsyncCallback(Actions.LiftInfo.MonitoringCallback), null);
                            break;
                        case Data.Enum.LiftType.扶梯:
                            if (null != Actions.LiftInfo.EscalatorMonitoring)
                                Actions.LiftInfo.EscalatorMonitoring.BeginInvoke(e, new AsyncCallback(Actions.LiftInfo.MonitoringCallback), null);
                            break;
                        case Data.Enum.LiftType.人行步道:
                            if (null != Actions.LiftInfo.TrailsMonittoring)
                                Actions.LiftInfo.TrailsMonittoring.BeginInvoke(e, new AsyncCallback(Actions.LiftInfo.MonitoringCallback), null);
                            break;
                    }
                }

                if (!e.IsRepeat || !InLineRunSign[e.LiftNum - 1])
                    if (null != Actions.LiftInfo.LiftRunInLine)
                    {
                        Actions.LiftInfo.LiftRunInLine.BeginInvoke(e, new AsyncCallback(Actions.LiftInfo.MonitoringCallback), null);
                    }
            }
            catch (Exception ex)
            {
                Log.WriteError(ex.Message, ex);
            }
        }

        public static bool isPlayingAlarm = false;
        /// <summary>
        /// 是否需要鸣警
        /// </summary>
        private static void CheckPlayAlarm(SerialDataEventArgs e)
        {
            lock (statusPark)
            {
                int[] park = (from int[] i in statusPark
                              where i[0] == e.LiftNum
                              select i).FirstOrDefault();

                if (e.Data.Fire || e.Data.FaultLamp || e.Data.CageAlarm || e.Data.ConverterAlarm || e.Earthquake)
                {

                    if (park != null)
                    {
                        Data.Enum.LiftStatus s = Data.Enum.LiftStatus.Normal;
                        s = (Data.Enum.LiftStatus)park[1];
                        if (s == Data.Enum.LiftStatus.Fire && e.Data.Fire)
                        {
                            park[2] = 1;
                        }
                        else if (s == Data.Enum.LiftStatus.Fault && e.Data.FaultLamp)
                        {
                            park[2] = 1;
                        }
                        else if (s == Data.Enum.LiftStatus.CageAlarm && e.Data.CageAlarm)
                        {
                            park[2] = 1;
                        }
                        else if (s == Data.Enum.LiftStatus.ConverterAlarm && e.Data.ConverterAlarm)
                        {
                            park[2] = 1;
                        }
                        else if (s == Data.Enum.LiftStatus.Earthquake && e.Earthquake)
                        {
                            park[2] = 1;
                        }
                        else
                        {
                            if (e.Data.Fire) park[1] = (int)Data.Enum.LiftStatus.Fire;
                            if (e.Data.FaultLamp) park[1] = (int)Data.Enum.LiftStatus.Fault;
                            if (e.Data.CageAlarm) park[1] = (int)Data.Enum.LiftStatus.CageAlarm;
                            if (e.Data.ConverterAlarm) park[1] = (int)Data.Enum.LiftStatus.ConverterAlarm;
                            if (e.Earthquake) park[1] = (int)Data.Enum.LiftStatus.Earthquake;
                            park[2] = 0;
                        }
                        if (park[2] != 1)
                        {
                            isPlayingAlarm = false;
                            PlayAlarm();
                        }
                    }
                    else
                    {
                        int[] i = new int[] { e.LiftNum, 0, 0 };

                        if (e.Data.Fire) i[1] = (int)Data.Enum.LiftStatus.Fire;
                        if (e.Data.FaultLamp) i[1] = (int)Data.Enum.LiftStatus.Fault;
                        if (e.Data.CageAlarm) i[1] = (int)Data.Enum.LiftStatus.CageAlarm;
                        if (e.Data.ConverterAlarm) i[1] = (int)Data.Enum.LiftStatus.ConverterAlarm;
                        if (e.Earthquake) i[1] = (int)Data.Enum.LiftStatus.Earthquake;

                        statusPark.Add(i);
                        PlayAlarm();
                    }
                }
                else
                {
                    if (park != null)
                    {
                        statusPark.Remove(park);
                    }
                }
            }
        }
        private static void PlayAlarm()
        {
            if (null == alarmPlayer)
            {
                isPlayingAlarm = false;
                alarmPlayer = new SoundPlayer();
                alarmPlayer.Stop();
                alarmPlayer.SoundLocation = @"Sound\alarm.wav";
            }
            if (!isPlayingAlarm)
            {
                isPlayingAlarm = true;
                alarmPlayer.PlayLooping();
            }
        }
        //stop alarm 声音接纳

        static DateTime start;
        static DateTime end;
        static void _spManager_SendCommandEvent(object sender, EventArgs e)
        {
            //续查
            System.Threading.Thread.Sleep(10);
            SendCommand((sender as SerialPortManager).Index, false);
        }
        //续查
        static void SendCommand(int index, bool isTimeOut)
        {
            if (index < 0) return;
            try
            {
                _portTimer[index] = -10;//.Stop();

                if (sports.Length < 1 || sports.Length < index + 1) return;
                SerialPortManager _spManager = sports[index];

                //电梯数组被查询的索引
                int _queryindex = queryIndex[index];
                if (liftnums[index].Length <= 0) return;

                if (_queryindex == 1 && index == 0) start = DateTime.Now;
                if (_queryindex >= liftnums[index].Length && index == 0)
                {
                    end = DateTime.Now;
                    //Log.WriteInf(ConsoleColor.Green, "--------------------轮循耗时:" + (end - start).TotalMilliseconds.ToString());
                }

                _queryindex = liftnums[index].Length > _queryindex ? _queryindex : 0;
                int _liftnum = liftnums[index][_queryindex];
                IEnumerable<Data.Model.LiftInfo> result = from r in Config.LiftInftList
                                                          where r.LiftNum == _liftnum
                                                          select r;
                Data.Enum.LiftType _liftType = result.First().LiftType;
                queryIndex[index] = ++_queryindex;
                commandData[index].Add(new CommandData(_spManager.PortName, Actions.SerialPorts.Query(_liftnum, _liftType)));

                if (null == commandData[index]) commandData[index] = new List<CommandData>();
                CommandData cd;
                if (commandData[index].Count < 1)
                {
                    cd = null;
                }
                else
                {
                    cd = commandData[index][0];
                    commandData[index].RemoveAt(0);
                }
                if (null == cd) return;
                _spManager.SendCommand(cd.Command, isTimeOut);
            }
            catch (Exception e)
            {
                Log.WriteError(e.Message, e);
                Init();
                //Actions.LiftInfo.ReLoadUI();
            }
            finally
            {
                try
                {
                    if (null != _portTimer && _portTimer.Length > index)
                        _portTimer[index] = 0;//.Start();
                }
                catch
                {
                    Init();// Actions.LiftInfo.ReLoadUI(); 
                }
            }
        }

        static void _spManager_SameDataEvent(object sender, SameDataEventArgs e)
        {
            ResetTimeOutTick(e.LiftNum);
        }

        private static void ResetTimeOutTick(int num)
        {
            int[] i = (from int[] r in _runOut
                       where r[0] == num
                       select r).FirstOrDefault();
            if (null != i)
                i[2] = 0;
        }

        /// <summary>
        /// 初始化委托 电梯控制
        /// </summary>
        /// <param name="type">控制类型 true有效,false解除</param>
        /// <param name="liftnum">电梯编号</param>
        /// <param name="port">COM端口</param>
        /// <param name="command">指令代码</param>
        /// <param name="floor">呼梯楼层1-99,command==0x01时有效</param>
        private static void doRegulate(bool type, int liftnum, string port, byte command, int floor)
        {
            try
            {
                List<byte> data = new List<byte>();
                data.Add(0xA1);
                if (type)
                    data.Add(0x13);//控制有效
                else
                    data.Add(0x10);//控制解除
                data.Add(command);//控制指令0x01呼梯,0x02锁梯,0x03高峰运行
                data.Add(byte.Parse(floor.ToString(), NumberStyles.HexNumber));
                data.Add(byte.Parse(liftnum.ToString()));
                data.Add(0x00);
                data.Add(0x09);

                int checksum = 0;
                for (int i = 1; i < 7; i++)
                {
                    checksum += int.Parse(data[i].ToString("X2"), NumberStyles.HexNumber);
                    if (checksum > 128) checksum %= 128;
                }
                data.Add(byte.Parse(checksum.ToString()));
                data.Add(0xD1);

                int index = PortMonitoring.sports.ToList().IndexOf((from p in PortMonitoring.sports
                                                                    where p.PortName == port
                                                                    select p).FirstOrDefault());
                index = index < 0 ? 0 : index;
                lock (PortMonitoring.commandData)
                {
                    PortMonitoring.commandData[index].RemoveAll(x => x.isRegulate == false);
                    //PortMonitoring.sports[index].Clear();
                    for (int i = 0; i < 2; i++)
                    {
                        PortMonitoring.commandData[index].Add(new CommandData(port, true, data.ToArray()));
                    }
                }
            }
            catch { }
        }

        /// <summary>
        /// 初始化委托 SMS监控
        /// </summary>
        /// <param name="liftnum"></param>
        /// <param name="ls"></param>
        /// <param name="time"></param>
        private static void doSMSMonitoring(SerialDataEventArgs args)
        {
            try
            {
                string phones = Settings.Default.SmsPhones;
                if (string.IsNullOrEmpty(phones)) return;

                byte[] data = new byte[2];

                string[] nums = phones.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (string phone in nums)
                {
                    SendCommand(phone, args.TimeRecieved, args.LiftNum, data);
                }
            }
            catch (Exception ex)
            {
                Log.WriteLog(Log.LogType.Error, ex.Message + " Send SMS Monitoring Error!");
            }
        }
        static void SendCommand(string phone, DateTime TimeRecieved, int liftnum, byte[] fault)
        {
            //A1	C1	C2	C3	D_1	D_2	D_3	D_4	D_5	D_6	D_7	D_8
            //D_9	D_10	D_11	D_12	D_13	D_14	D_15	D_16	Len1	Len2	Sum	D1

            byte[] data = new byte[24];
            data[0] = 0xA1;
            data[1] = 0x55;
            data[2] = fault[0];
            data[3] = fault[1];
            data[4] = byte.Parse(liftnum.ToString(), NumberStyles.Integer);
            byte[] tmp = phone.toByteArray();
            Array.Copy(tmp, 0, data, 5, tmp.Length);

            byte[] time = TimeRecieved.ToString("yyyyMMddHHmmss").toByteArray();
            Array.Copy(time, 0, data, 11, time.Length);

            data[20] = 0x00;
            data[21] = 0x24;
            int checksum = 0;
            for (int i = 1; i < 22; i++)
            {
                checksum += int.Parse(data[i].ToString("X2"), NumberStyles.HexNumber);
                if (checksum > 128) checksum %= 128;
            }
            data[22] = byte.Parse(checksum.ToString());
            data[23] = 0xD1;


            int index = PortMonitoring.sports.ToList().IndexOf((from p in PortMonitoring.sports
                                                                where p.PortName == "COM1"
                                                                select p).FirstOrDefault());
            index = index < 0 ? 0 : index;
            PortMonitoring.sports[index].SendCommand(data.ToArray());

        }


        static int seconds = 0;
        /// <summary>
        /// 时钟
        /// </summary>
        static void timer_Tick(object sender, EventArgs e)
        {
            #region 超时机制
            //每秒计时一次
            seconds++;
            if (10 <= seconds)
            {
                if (null != _runOut)
                {
                    foreach (int[] i in _runOut)
                    {
                        if (null != i)
                        {
                            i[2]++;
                            if (i[2] >= timeOutStick)
                            {
                                i[2] = 0;
                                if (PortMonitoring.LiftRunSign[i[0] - 1])
                                {
                                    if (null != Actions.LiftInfo.LiftRunNC) Actions.LiftInfo.LiftRunNC(i[0], i[1]);
                                    if (null != Actions.LiftInfo.LiftRunNCInLine) Actions.LiftInfo.LiftRunNCInLine(i[0]);
                                    if (null != Actions.FaultLog.AddNC) Actions.FaultLog.AddNC.BeginInvoke(i[0], new AsyncCallback(Actions.FaultLog.AddNCCallback), null);
                                }
                            }
                        }
                    }
                }
                seconds = 0;
            }
            #endregion

            if (null != _portTimer)
            {
                for (int i = 0; i < _portTimer.Length; i++)
                {
                    _portTimer[i]++;
                    if (_portTimer[i] >= 3)//发送接收超时
                    {
                        _portTimer[i] = 0;
                        SendCommand(i, true);
                    }
                }
            }
        }

        public static void Dispose()
        {
            timer.Stop();
            timer.Dispatcher.BeginInvokeShutdown(DispatcherPriority.Normal);
            foreach (var p in sports)
            {
                p.Dispose();
            }
        }

        internal static void Stop()
        {
            timer.Stop();
            foreach (var p in sports)
            {
                p.NewSerialDataRecieved -= new SerialPortDataReceivedEventHandler(_spManager_NewSerialDataRecieved);
                p.Dispose();
            }
        }
    }


    public class CommandData
    {
        public CommandData(string _portname, byte[] _buffer)
        {
            this.PortName = _portname;
            this.Command = _buffer;
            this.isRegulate = false;
        }
        public CommandData(string _portname, bool _isregulate, byte[] _buffer)
        {
            this.PortName = _portname;
            this.isRegulate = _isregulate;
            this.Command = _buffer;
        }
        public string PortName { get; set; }
        public bool isRegulate { get; set; }
        public byte[] Command { get; set; }
    }
}
